package net.minecraft.src;

import java.util.*;

public class BlockSticky extends  Block
{ 

	class  SavePossibleIteration
	{
		public int  direction;
		public int  possibleCount;
	}
	private final int   maxSticky = 15;
	private static Map < Coords ,StickyProximityArea >  coordsToProximmity = new HashMap < Coords ,StickyProximityArea >();
	private static Set < Coords >  updated =  new HashSet< Coords >   ();
	//potrzebna mapa kierunku
	//map< Integral, int[] >directionModyficators;
	// mapa  opoasite  blocks
	//map< Integral, Integral >opositeBlocks;
	private static Set < Coords >  scheduledUpdate =  new HashSet< Coords >   ();
	
	private static Map < Coords ,SavePossibleIteration > savedPossibilities = new HashMap  < Coords ,SavePossibleIteration >();
	
	private static List < Set<Coords> >  neighboursList =  new ArrayList< Set<Coords> >();
	
	
	
	private static List < Set<Coords> >  neighboursBlocadeList =  new ArrayList< Set<Coords> >();
	
	
	
	private  static Map< Integer ,int []  > shifts = new HashMap< Integer,int [] >();
		static {
		shifts.put( new Integer(0), new int[] {-1,0,0} );
		shifts.put( new Integer(1), new int[] {1,0,0} );
		shifts.put( new Integer(2), new int[] {0,-1,0} );
		shifts.put( new Integer(3), new int[] {0,1,0} );
		shifts.put( new Integer(4), new int[] {0,0,-1} );
		shifts.put( new Integer(5), new int[] {0,0,1} );
		}
		
	private  static Map< Integer ,Integer >  localToGlobalDirection  = new HashMap< Integer,Integer >(); 
	static {
		localToGlobalDirection.put( new Integer(0), new Integer(4) );
		localToGlobalDirection.put( new Integer(1), new Integer(5) );
		localToGlobalDirection.put( new Integer(2), new Integer(1) );
		localToGlobalDirection.put( new Integer(3), new Integer(0) );
		localToGlobalDirection.put( new Integer(4), new Integer(2) );
		localToGlobalDirection.put( new Integer(5), new Integer(3));
	}
		
	private  static Map< Integer ,Integer >  globalToLocalDirection  = new HashMap< Integer,Integer >(); 
	static {
		globalToLocalDirection.put( new Integer(4), new Integer(0) );
		globalToLocalDirection.put( new Integer(5), new Integer(1) );
		globalToLocalDirection.put( new Integer(1), new Integer(2) );
		globalToLocalDirection.put( new Integer(0), new Integer(3) );
		globalToLocalDirection.put( new Integer(2), new Integer(4) );
		globalToLocalDirection.put( new Integer(3), new Integer(5));
	}
	
	private  static Map< Integer ,Integer >  localToPistonDirection  = new HashMap< Integer,Integer >(); 
	static {
		localToPistonDirection.put( new Integer(0), new Integer(5) );
		localToPistonDirection.put( new Integer(1), new Integer(4) );
		localToPistonDirection.put( new Integer(2), new Integer(1) );
		localToPistonDirection.put( new Integer(3), new Integer(0) );
		localToPistonDirection.put( new Integer(4), new Integer(3) );
		localToPistonDirection.put( new Integer(5), new Integer(2));
	}
		
    protected BlockSticky(int i)
    {
        super(i, Material.rock);
        	
    }    

   


public  boolean isConductiveSticky( int id )
{
return 
		    mod_BlockSticky.stoneConductiveStickyIdIdle == id
		||  mod_BlockSticky.stoneConductiveStickyIdActive  == id;
}



public  boolean isPoweringTo(IBlockAccess iblockaccess, int i, int j, int k, int l){
	
	if ( findInGlobal(  i, j, k,neighboursList ) )
	return  false; 
	
	int direction = globalToLocalDirection.get(l);
	int [] localShift = shifts.get(direction);
	
	int  thisId= iblockaccess.getBlockId(i, j, k);
	int  callId =  iblockaccess.getBlockId(i+localShift[0], j+localShift[1], k+localShift[2]);
	if ( isValidKind( callId ) )
	{
		int  meta = iblockaccess.getBlockMetadata(i+localShift[0], j+localShift[1], k+localShift[2]);
		if( meta == l)
			return false;
	}
	else if( callId == blockID )
	{
		callId = iblockaccess.getBlockId(i+2*localShift[0], j+2*localShift[1], k+2*localShift[2]);
		if ( isValidKind( callId ) )
		{
			int  meta = iblockaccess.getBlockMetadata(i+2*localShift[0], j+2*localShift[1], k+2*localShift[2]);
			
			if( meta == l)
				return false;
		}		
	}
	
	return
	mod_BlockSticky.stoneConductiveStickyIdActive == iblockaccess.getBlockMetadata(i, j, k);
} 
private boolean func_30002_h(World world, int i, int j, int k)
{
    if ( world.isBlockIndirectlyProvidingPowerTo(i, j + 1, k, 1))
    {
        return true;
    }
    if ( world.isBlockIndirectlyProvidingPowerTo(i, j - 1, k, 0))
    {
        return true;
    }
    if (world.isBlockIndirectlyProvidingPowerTo(i, j, k - 1, 2))
    {
        return true;
    }
    if (world.isBlockIndirectlyProvidingPowerTo(i, j, k + 1, 3))
    {
        return true;
    }
    if (world.isBlockIndirectlyProvidingPowerTo(i - 1, j, k, 4))
    {
        return true;
    }
    return world.isBlockIndirectlyProvidingPowerTo(i + 1, j, k, 5);


}
private boolean  checkIfRedstoneIsNeighbour(World world, int par1, int par2, int par3)
{
	boolean  present = false;
	present = present || world.getBlockId(par1 - 1, par2, par3) ==  Block.redstoneWire.blockID;
	present = present || world.getBlockId(par1 + 1, par2, par3) ==  Block.redstoneWire.blockID;
	present = present || world.getBlockId(par1, par2 - 1, par3) ==  Block.redstoneWire.blockID;
	present = present || world.getBlockId(par1, par2 + 1, par3) ==  Block.redstoneWire.blockID;
	present = present || world.getBlockId(par1, par2, par3 - 1) ==  Block.redstoneWire.blockID;
	present = present || world.getBlockId(par1, par2, par3 + 1) ==  Block.redstoneWire.blockID;
	
	
	return  present;
}

    
private void notifySouroundings(World world, int i, int j, int k)
{
	if(
			world.getBlockMetadata(i, j, k) == mod_BlockSticky.stoneConductiveStickyIdActive
		&&	checkIfRedstoneIsNeighbour(world, i,j,k)	
	  )
	{
		world.notifyBlocksOfNeighborChange(i, j, k, blockID);

	}
}


    public int getBlockTextureFromSideAndMetadata(int i, int j) {

			switch( j )
			{
			case  mod_BlockSticky.brickStickyId:
				return mod_BlockSticky.brickStickyTextureId;				
			case  mod_BlockSticky.planksStickyId:
				return mod_BlockSticky.planksStickyTextureId;
			case  mod_BlockSticky.glassStickyId:
				return mod_BlockSticky.glassStickyTextureId;
			case  mod_BlockSticky.stoneStickyId:
				return mod_BlockSticky.stoneStickyTextureId;
			case  mod_BlockSticky.stoneBrickStickyId:
				return mod_BlockSticky.stoneBricksStickyTextureId;	
			case  mod_BlockSticky.cobleStoneStickyId:
				return mod_BlockSticky.cobleStoneStickyTextureId;
			case  mod_BlockSticky.stoneConductiveStickyIdActive:
				return mod_BlockSticky.stoneConductiveStickyTextureId;
			case  mod_BlockSticky.stoneConductiveStickyIdIdle:
				return mod_BlockSticky.stoneConductiveStickyTextureId;
			}

		return -1;
    }

    public void onBlockPlacedBy(World world, int i, int j, int k, EntityLiving entityliving)
    {
          ///  world.setBlockMetadataWithNotify(i, j, k, 2);
    }


    public void moveBlockByID ( World  world, int i, int j, int k, int directionOfMove, int  meta, int id )
    {
    	
    	int[] shift =shifts.get(directionOfMove);
    	
    	if(world.getBlockId(i , j , k) == 0 )
    		return;
    	
    	world.setBlockWithNotify(i , j , k , 0);
    	
		world.setBlockAndMetadata(i + shift[0], j + shift[1], k + shift[2], Block.pistonMoving.blockID, meta);
		world.setBlockTileEntity(i + shift[0], j + shift[1], k + shift[2], BlockPistonMoving.getTileEntity(id, meta, localToGlobalDirection.get(directionOfMove), true, false));   
		
		if( id  == blockID )
		{
			tryDragTile(directionOfMove, world, i, j, k);
		} 	
    }
    
    public void  moveBlock(World  world, int i, int j, int k, int directionOfMove )
    {
    	int[] shift =shifts.get(directionOfMove);
    	int meta = world.getBlockMetadata(i, j, k);
    	int storeId = world.getBlockId(i , j , k);
    	
    	if(
    			storeId == 0
    		||	storeId == Block.pistonMoving.blockID
    	   )
    		return;
    	
    	world.setBlockWithNotify(i , j , k , 0);
    	
		world.setBlockAndMetadata(i + shift[0], j + shift[1], k + shift[2], Block.pistonMoving.blockID, meta);
		world.setBlockTileEntity(i + shift[0], j + shift[1], k + shift[2], BlockPistonMoving.getTileEntity(storeId, meta, localToGlobalDirection.get(directionOfMove), true, false));   
		
		if( storeId  == blockID )
		{
			tryDragTile(directionOfMove, world, i, j, k);
		}
	}
    
    
    
    public void  checkIfPossibleMoveWorld ( World world, int i, int j, int k )
    {
    	
    	
    	Coords coords = new  Coords(i , j, k );
    	if ( !savedPossibilities.containsKey(coords ) )
    		return;
    	SavePossibleIteration  possible =  savedPossibilities.get(coords);
    	savedPossibilities.remove(coords);
    	int[] shift =shifts.get(possible.direction);
    	if ( world.getBlockId(i +shift[0] , j + shift[1] , k + shift[2]) == 0 )
    	{
    		possible.possibleCount --;
    		if ( possible.possibleCount > 0 )
    		{
    			savedPossibilities.put(coords, possible);
    			
    		}
    		int  meta = world.getBlockMetadata(i,  j,  k);
    		moveBlock(  world,  i,  j,  k, 1 );
    	 
    	}
   
    }
    
	
    public void onBlockAdded(World world, int i, int j, int k)
    {
			super.onBlockAdded(world, i, j, k);	
			
		  //  checkIfPossibleMoveWorld (  world,  i,  j,  k );
			
    }

    public void onBlockRemoval(World world, int i, int j, int k)
    {
    		super.onBlockRemoval(world, i, j, k);
    }
    public void updateTick(World world, int i, int j, int k, Random random)
    {	
    	int  meta = world.getBlockMetadata(i, j, k);
    	
    	Coords coords = new Coords(i, j ,k);
    	if( !scheduledUpdate.contains(coords)  )
    		return;
    	
    	scheduledUpdate.remove(coords);
    	
    	if( meta == mod_BlockSticky.stoneConductiveStickyIdActive  )
    		world.setBlockMetadataWithNotify(i, j, k, mod_BlockSticky.stoneConductiveStickyIdIdle);
    	else if ( meta == mod_BlockSticky.stoneConductiveStickyIdIdle  )
    		world.setBlockMetadataWithNotify(i, j, k, mod_BlockSticky.stoneConductiveStickyIdActive);
    	
    	
    	
    }

    public void  triggerFromSticky()
    {
    	
    }
    
    
    public boolean renderAsNormalBlock()
    {
        return false;
    }
    private boolean isPistonCaonnected( int id )
    {
    	return true;
    }
    
    private void  fillProximityForThisBlock(World world,int i, int j, int k)
    {
    	Coords   coords = new Coords(i, j, k);
    	for( int m = 0; m < 6; ++m )
    	{
    		
    		int [] proximity =  getProximityInDirection( world,i, j, k, m );
    		if( proximity != null )
    		{
    			setProximityInDirection( coords, proximity ,m );
    			
    		}
    	}
    	
    }
    
    protected int damageDropped(int i) {
        return i;
    }
    private void removeProximityForThisBlock(World world,int i, int j, int k)
    {
    	Coords   coords = new Coords(i, j, k);
    	if ( coordsToProximmity.containsKey(coords) )
    	{
    		coordsToProximmity.remove(coords);
    	}
    }
    
    private boolean  findOnSide(World world,int i, int j, int k, int direction)
    {
    	int  m = 0;
    	
    	int[] shift =shifts.get(direction);
    	
    	int  possibleIteration  = 0;
    	while(true)
    	{
    		m++;
    		int id  = world.getBlockId(i +shift[0]*m, j+ shift[1]*m, k + shift[2]*m);
    		if( m == 1 
    			&& id  != 0
    		 )
    			return  false;
    			
    		
    		
    		if(id == 0 )
    		{
    			
    			if ( maxSticky <= m )
    				break;
    			else
    				continue;
    		
    		}

    		else if ( id == pistonMoving.blockID )
    		{
    			int  secondId ;
    			TileEntity  tile = world.getBlockTileEntity(i +shift[0]*m, j+ shift[1]*m, k + shift[2]*m); 
    			assert(tile != null);
    			secondId = (( TileEntityPiston )tile).getStoredBlockID();
    			int orientation  = (( TileEntityPiston )tile).getBlockMetadata();


    			
    			if(    isValidKind( secondId )
    				&& ( localToPistonDirection.get(direction) == orientation )
    				&& m > 1
    			  )
    			{
    				//int  previous  = world.getBlockId(i +shift[0]*(m-1), j+ shift[1]*(m-1), k + shift[2]*(m-1));
    				/*if (
    						0 == previous
    					   || previous == pistonMoving.blockID
    						)*/
    				moveBlock(  world,  i,  j,  k, direction );
    				possibleIteration++;
    				continue;
    			}
    			else
    			{
    				continue;
    			}	
    				
    		}

    		return  false;
    	}
	/*	if ( possibleIteration> 0 )
		{
			SavePossibleIteration  possible  = new SavePossibleIteration();
			possible.direction = direction;
			possible.possibleCount = possibleIteration;
			Coords coords = new  Coords(i +shift[0], j+ shift[1], k + shift[2]);
			savedPossibilities.put(coords, possible);
		//	moveBlock(  world,  i,  j,  k, direction );
			return  true;
		}*/	
    	return  false;
    }

    
    private void determineCondition(World world,int i, int j, int k, int direction)
    {
    	int  modifDirection = direction;
    	if( modifDirection % 2 == 1 )
    		modifDirection--; 
    	else
    		modifDirection++;
    	
    	int[] shift =shifts.get(modifDirection);
    	
    	int[] stickowo = getProximityInDirection( world,i, j, k, modifDirection );
    
    	if (stickowo == null )
    		return;
    	
    	if( stickowo[0] == blockID )
    		moveBlock(world, i +shift[0], j+ shift[1], k + shift[2], direction );
    	else if( stickowo[1] == blockID )
    		moveBlock(world, i +shift[0]*2, j+ shift[1]*2, k + shift[2]*2, direction );
    	
    }
    
    private void  setProximityInDirection( Coords   coords, int []  ids ,int direction )
    {
    	int[] shift =shifts.get(direction);
    	Coords newCoords = new Coords(coords.x + shift[0],coords.y + shift[1],coords.z + shift[2]);
    	
    	if ( coordsToProximmity.containsKey(coords) )
    	{
    		coordsToProximmity.get(coords).addProximity(newCoords,ids);
    	}
    	else
    	{
    		StickyProximityArea  proximityArea  = new StickyProximityArea();
    		proximityArea.addProximity(newCoords,ids);
    		coordsToProximmity.put(coords, proximityArea);
    	}

    }
    /* 0 -x 1 x 2 -y 3 y 4 -z 5 z  */
    private  int[]  getProximityInDirection( World world,int i, int j, int k, int direction )
    {
    	int[] ids = new  int [3];
    	int[] shift =shifts.get(direction);
    	boolean  valid = false;
    	for ( int m = 0; m < 3   ; ++m )
    	{
    		i +=shift[0];
    		j += shift[1];
    		k += shift[2];
    		ids [m]  = world.getBlockId(i, j, k ); 
    		
    		valid = valid || isValidKind( ids [m] );
    	}
     
    	if(valid)
    		return ids;  
    	else
    		return null;
    }
    
    private  boolean  isValidKind( int  id )
    {
       	return 

            	    pistonBase.blockID == id
            	||  pistonStickyBase.blockID == id;
       	
    
    }
    
    private void  tryDragTile(int  direction, World world,int i,int j , int k  )
    {
    	/* exception of lava, woda */
    	int [] shift = shifts.get(direction);
    	
    	int   id  = world.getBlockId(i  - shift[0], j - shift[1], k - shift[2]);
    	if (
    			   Block.blockSnow.blockID == id
    			|| Block.sand.blockID == id
    			|| 0 == id
    			|| pistonMoving.blockID == id 
    			|| blockID == id
    			|| pistonExtension.blockID == id
    		)
    		return;
    	/*

    	}
    	*/
    	if(
    				id == pistonBase.blockID 
            	||  id == pistonStickyBase.blockID 
            	
            	
    		)
    	{
    		int  ie, je, ke;
    		ie = i  - shift[0] * 2; 
    		je = j - shift[1] * 2;
    		ke = k - shift[2] * 2;
    		
    		if( pistonExtension.blockID  == world.getBlockId(ie,je ,ke ) )
    		{
    			
    	    	int meta = world.getBlockMetadata(ie,je ,ke);

    	    	moveBlock( world, i  - shift[0], j - shift[1], k - shift[2], direction );
    	    	
    			world.setBlockAndMetadata(ie+ shift[0],je + shift[1],ke+ shift[2], Block.pistonMoving.blockID, meta);
    			world.setBlockTileEntity(ie+ shift[0],je + shift[1],ke+ shift[2], BlockPistonMoving.getTileEntity(pistonExtension.blockID, meta, localToGlobalDirection.get(direction), true, false)); 
    			return ;
    
    		}
    		
    		
    	}
    		
    	
    	moveBlock( world, i  - shift[0], j - shift[1], k - shift[2], direction );
    }
  
    
    private void  compareCurrentWithSaved(World  world, int i, int j ,int k)
    {


    	Coords  coords = new Coords (i, j ,k);
    	
    	StickyProximityArea  proximityArea;
    	
    	
    	int[] shift;
    	for( int m = 0; m < 6; ++m )
    	{
    		if ( findOnSide( world, i,  j,  k,  m) )
    			break;
    	}


    }
    
    private  void  addToNeighBours( World world, int i, int j, int k, Set < Coords >  neighbours  )
    {
    	if( 
    			world.getBlockId(i, j , k) != mod_BlockSticky.stickyId 
    		||  !isConductiveSticky( world.getBlockMetadata(i, j , k) )	
    	  )
    		return ;
    	Coords coords = new  Coords ( i, j , k );
    	
    	if( neighbours.contains( coords ) )
    		return;
    	
    	neighbours.add(coords);
    	
    	
    	addToNeighBours(world,i, j - 1, k,neighbours ) ;
    	addToNeighBours(world,i, j, k - 1,neighbours );
    	addToNeighBours(world,i, j, k + 1,neighbours );
    	addToNeighBours(world,i - 1, j, k,neighbours );
    	addToNeighBours(world,i + 1, j, k,neighbours );
    	addToNeighBours(world,i, j + 1, k,neighbours );
    }

    
    
    private boolean isBlockIdPistonKind(World world, int i, int j, int k)
    {
    	
    	int  id = world.getBlockId(i, j - 1, k) ;
    	id = world.getBlockId(i, j, k - 1);
    	id = world.getBlockId(i, j, k + 1);
    	id = world.getBlockId(i - 1, j, k);
    	id = world.getBlockId(i + 1, j, k);
    	id = world.getBlockId(i, j + 1, k);

    	return  false;
    }
    
    
   private  void notifyAll(World world, Set<Coords> neighbours )
   {	   
		Iterator<Coords> setIterator = neighbours.iterator();
		
		while(setIterator.hasNext())
		{ 
			Coords  coordsInSet=setIterator.next();
			notifySouroundings(world, coordsInSet.x, coordsInSet.y, coordsInSet.z);		
		}
		
   }
    
   private  boolean checkEnergyForAll(World world, Set<Coords> neighbours )
   {	   
		Iterator<Coords> setIterator = neighbours.iterator();
		boolean  isEnergy = false;
		while(setIterator.hasNext())
		{ 
			Coords  coordsInSet=setIterator.next();
			isEnergy = isEnergy || func_30002_h(world, coordsInSet.x, coordsInSet.y, coordsInSet.z);
			
		}
		return isEnergy;
		
   }
   
  private  boolean  findInGlobal( int i, int j, int k,List < Set<Coords> >  neighboursList  )
  {
	  boolean  isPresent = false;
	  Coords  coords = new Coords ( i , j ,k );
	  
		Iterator<Set<Coords>> setIterator = neighboursList.iterator();
		while(setIterator.hasNext())
		{ 
			isPresent = isPresent || setIterator.next().contains(coords);	
		}
		
		return  isPresent ;
  }
   
    
    public void onNeighborBlockChange(World world, int i, int j, int k, int l)
    {	
    	
    	int  meta = world.getBlockMetadata(i, j, k);
    	if ( isConductiveSticky( meta ) )
    	{
    		if( findInGlobal(i, j, k,neighboursBlocadeList) )
    			return;
    		
    		Set<Coords> neighbours = new  HashSet<Coords> ();
    		
    		addToNeighBours( world, i, j, k, neighbours  );
    		neighboursList.add(neighbours);
    		neighboursBlocadeList.add(neighbours);
    		
    		assert( neighbours !=  null );
    		assert( neighbours.size() >  0 );		
    		notifyAll(world, neighbours);
    		boolean  power  =  checkEnergyForAll(world, neighbours);
    		        	
        	
        	if( 
        			meta == mod_BlockSticky.stoneConductiveStickyIdActive
        		&&  power == false	
        			)
        	{
        		scheduledUpdate.add( new Coords (i, j, k));
        		world.scheduleBlockUpdate(i, j, k, blockID, 4);
        		
        	}
        	else if (
        			meta == mod_BlockSticky.stoneConductiveStickyIdIdle  
        		&&  power == true	
        			)
        	{
        		scheduledUpdate.add( new Coords (i, j, k));
        		world.scheduleBlockUpdate(i, j, k, blockID, 4);    
        	}

    		neighboursList.remove(neighbours);
    		
    		notifyAll(world, neighbours);
    		
    		neighboursBlocadeList.remove(neighbours);
    	}	
    	compareCurrentWithSaved( world, i, j , k);
    	

    }
    
    public int idDropped(int i, Random random, int j)
    {
        return mod_BlockSticky.stickyItem.shiftedIndex;
    }
    
    public boolean isOpaqueCube()
    {
        return false;
    }
    
}

/*
*/